/*
 * Copyright (c) 2006-2021, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2008-08-14     Bernard      the first version
 * 2010-02-15     Gary Lee     add strlcpy
 * 2010-03-17     Bernard      add strlcpy implementation to this file.
 *                             fix strlcpy declaration
 * 2010-03-24     Bernard      add strchr and strtok implementation.
 */

#include <rtthread.h>
#include <stdint.h>

#if !defined (RT_USING_NEWLIB) && defined (RT_USING_MINILIBC)
#include "string.h"

/* there is no strcpy and strcmp implementation in RT-Thread */
char *strcpy(char *dest, const char *src)
{
    return (char *)rt_strncpy(dest, src, rt_strlen(src) + 1);
}

char *strncpy(char *dest, const char *src, size_t siz)
{
    return (char *)rt_strncpy(dest, src, siz);
}

size_t strlcpy(char *dst, const char *src, size_t siz)
{
    register char *d = dst;
    register const char *s = src;
    register size_t n = siz;

    /* Copy as many bytes as will fit */
    if (n != 0 && --n != 0)
    {
        do
        {
            if ((*d++ = *s++) == 0) break;
        } while (--n != 0);
    }

    /* Not enough room in dst, add NUL and traverse rest of src */
    if (n == 0)
    {
        if (siz != 0) *d = '\0';    /* NUL-terminate dst */
        while (*s++) ;
    }

    return(s - src - 1);            /* count does not include NUL */
}

int strcmp (const char *s1, const char *s2)
{
    while (*s1 && *s1 == *s2)
        s1++, s2++;
    return (*s1 - *s2);
}

/**
 * strncmp - Compare two length-limited strings
 * @cs: One string
 * @ct: Another string
 * @count: The maximum number of bytes to compare
 */
int strncmp(const char *cs,const char *ct, size_t count)
{
    register signed char __res = 0;

    while (count) {
        if ((__res = *cs - *ct++) != 0 || !*cs++)
            break;
        count--;
    }

    return __res;
}

char *strcat(char * dest, const char * src)
{
    char *tmp = dest;

    while (*dest)
        dest++;
    while ((*dest++ = *src++) != '\0')
        ;

    return tmp;
}

char *strncat(char *dest, const char *src, size_t count)
{
    char *tmp = dest;

    if (count) {
        while (*dest)
            dest++;
        while ((*dest++ = *src++)) {
            if (--count == 0) {
                *dest = '\0';
                break;
            }
        }
    }

    return tmp;
}

char *strrchr(const char *t, int c)
{
    register char ch;
    register const char *l=0;

    ch = c;
    for (;;)
    {
        if (*t == ch) l=t;
        if (!*t) return (char*)l;
        ++t;
    }

    return (char*)l;
}


int  strncasecmp ( const char* s1, const char* s2, size_t len )
{
    register unsigned int  x2;
    register unsigned int  x1;
    register const char*   end = s1 + len;

    while (1)
    {
        if ((s1 >= end) )
            return 0;

        x2 = *s2 - 'A'; if ((x2 < 26u)) x2 += 32;
        x1 = *s1 - 'A'; if ((x1 < 26u)) x1 += 32;
        s1++; s2++;

        if (x2 != x1)
            break;

        if (x1 == (unsigned int)-'A')
            break;
    }

    return x1 - x2;
}

/* private function */
#define isdigit(c)  ((unsigned)((c) - '0') < 10)

rt_inline int divide(int *n, int base)
{
    rt_int32_t res;

    /* optimized for processor which does not support divide instructions. */
    if (base == 10)
    {
        res = ((int)*n) % 10U;
        *n = ((int)*n) / 10U;
    }
    else
    {
        res = ((int)*n) % 16U;
        *n = ((int)*n) / 16U;
    }

    return res;
}

rt_inline int skip_atoi(const char **s)
{
    register int i=0;
    while (isdigit(**s)) i = i*10 + *((*s)++) - '0';

    return i;
}

unsigned char _ctype[] = {
_C,_C,_C,_C,_C,_C,_C,_C,            /* 0-7 */
_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,     /* 8-15 */
_C,_C,_C,_C,_C,_C,_C,_C,            /* 16-23 */
_C,_C,_C,_C,_C,_C,_C,_C,            /* 24-31 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,            /* 32-39 */
_P,_P,_P,_P,_P,_P,_P,_P,            /* 40-47 */
_D,_D,_D,_D,_D,_D,_D,_D,            /* 48-55 */
_D,_D,_P,_P,_P,_P,_P,_P,            /* 56-63 */
_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,  /* 64-71 */
_U,_U,_U,_U,_U,_U,_U,_U,            /* 72-79 */
_U,_U,_U,_U,_U,_U,_U,_U,            /* 80-87 */
_U,_U,_U,_P,_P,_P,_P,_P,            /* 88-95 */
_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,  /* 96-103 */
_L,_L,_L,_L,_L,_L,_L,_L,            /* 104-111 */
_L,_L,_L,_L,_L,_L,_L,_L,            /* 112-119 */
_L,_L,_L,_P,_P,_P,_P,_C,            /* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,        /* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,        /* 144-159 */
_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */

#define __ismask(x) (_ctype[(int)(unsigned char)(x)])

#define isalnum(c)  ((__ismask(c)&(_U|_L|_D)) != 0)
#define isalpha(c)  ((__ismask(c)&(_U|_L)) != 0)
#define iscntrl(c)  ((__ismask(c)&(_C)) != 0)
#define isgraph(c)  ((__ismask(c)&(_P|_U|_L|_D)) != 0)
#define islower(c)  ((__ismask(c)&(_L)) != 0)
#define isprint(c)  ((__ismask(c)&(_P|_U|_L|_D|_SP)) != 0)
#define ispunct(c)  ((__ismask(c)&(_P)) != 0)
#define isspace(c)  ((__ismask(c)&(_S)) != 0)
#define isupper(c)  ((__ismask(c)&(_U)) != 0)
#define isxdigit(c) ((__ismask(c)&(_D|_X)) != 0)

#define isascii(c) (((unsigned char)(c))<=0x7f)
#define toascii(c) (((unsigned char)(c))&0x7f)

static inline unsigned char __tolower(unsigned char c)
{
    if (isupper(c))
        c -= 'A'-'a';
    return c;
}

static inline unsigned char __toupper(unsigned char c)
{
    if (islower(c))
        c -= 'a'-'A';
    return c;
}

int tolower(int c)
{
    return __tolower(c);
}

int toupper(int c)
{
    return __toupper(c);
}

/**
 * simple_strtoul - convert a string to an unsigned long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
    unsigned long result = 0,value;

    if (!base) {
        base = 10;
        if (*cp == '0') {
            base = 8;
            cp++;
            if ((toupper(*cp) == 'X') && isxdigit(cp[1])) {
                cp++;
                base = 16;
            }
        }
    } else if (base == 16) {
        if (cp[0] == '0' && toupper(cp[1]) == 'X')
            cp += 2;
    }
    while (isxdigit(*cp) &&
           (value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
        result = result*base + value;
        cp++;
    }
    if (endp)
        *endp = (char *)cp;
    return result;
}

/**
 * simple_strtol - convert a string to a signed long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long simple_strtol(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -simple_strtoul(cp+1,endp,base);
    return simple_strtoul(cp,endp,base);
}

/**
 * simple_strtoull - convert a string to an unsigned long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
unsigned long long simple_strtoull(const char *cp,char **endp,unsigned int base)
{
    unsigned long long result = 0, value;

    if (*cp == '0') {
        cp++;
        if ((toupper(*cp) == 'X') && isxdigit (cp[1])) {
            base = 16;
            cp++;
        }
        if (!base) {
            base = 8;
        }
    }
    if (!base) {
        base = 10;
    }
    while (isxdigit (*cp) && (value = isdigit (*cp)
                ? *cp - '0'
                : (islower (*cp) ? toupper (*cp) : *cp) - 'A' + 10) < base) {
        result = result * base + value;
        cp++;
    }
    if (endp)
        *endp = (char *) cp;
    return result;
}

/**
 * simple_strtoll - convert a string to a signed long long
 * @cp: The start of the string
 * @endp: A pointer to the end of the parsed string will be placed here
 * @base: The number base to use
 */
long long simple_strtoll(const char *cp,char **endp,unsigned int base)
{
    if(*cp=='-')
        return -simple_strtoull(cp+1,endp,base);
    return simple_strtoull(cp,endp,base);
}

/**
 * vsscanf - Unformat a buffer into a list of arguments
 * @buf:    input buffer
 * @fmt:    format of buffer
 * @args:   arguments
 */
int vsscanf(const char * buf, const char * fmt, va_list args)
{
    const char *str = buf;
    char *next;
    int num = 0;
    int qualifier;
    int base;
    int field_width = -1;
    int is_sign = 0;

    while(*fmt && *str) {
        /* skip any white space in format */
        /* white space in format matchs any amount of
         * white space, including none, in the input.
         */
        if (isspace(*fmt)) {
            while (isspace(*fmt))
                ++fmt;
            while (isspace(*str))
                ++str;
        }

        /* anything that is not a conversion must match exactly */
        if (*fmt != '%' && *fmt) {
            if (*fmt++ != *str++)
                break;
            continue;
        }

        if (!*fmt)
            break;
        ++fmt;

        /* skip this conversion.
         * advance both strings to next white space
         */
        if (*fmt == '*') {
            while (!isspace(*fmt) && *fmt)
                fmt++;
            while (!isspace(*str) && *str)
                str++;
            continue;
        }

        /* get field width */
        if (isdigit(*fmt))
            field_width = skip_atoi(&fmt);

        /* get conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z') {
            qualifier = *fmt;
            fmt++;
        }
        base = 10;
        is_sign = 0;

        if (!*fmt || !*str)
            break;

        switch(*fmt++) {
        case 'c':
        {
            char *s = (char *) va_arg(args,char*);
            if (field_width == -1)
                field_width = 1;
            do {
                *s++ = *str++;
            } while(field_width-- > 0 && *str);
            num++;
        }
        continue;
        case 's':
        {
            char *s = (char *) va_arg(args, char *);
            if(field_width == -1)
                field_width = INT_MAX;
            /* first, skip leading white space in buffer */
            while (isspace(*str))
                str++;

            /* now copy until next white space */
            while (*str && !isspace(*str) && field_width--) {
                *s++ = *str++;
            }
            *s = '\0';
            num++;
        }
        continue;
        case 'n':
            /* return number of characters read so far */
        {
            int *i = (int *)va_arg(args,int*);
            *i = str - buf;
        }
        continue;
        case 'o':
            base = 8;
            break;
        case 'x':
        case 'X':
            base = 16;
            break;
        case 'd':
        case 'i':
            is_sign = 1;
        case 'u':
            break;
        case '%':
            /* looking for '%' in str */
            if (*str++ != '%')
                return num;
            continue;
        default:
            /* invalid format; stop here */
            return num;
        }

        /* have some sort of integer conversion.
         * first, skip white space in buffer.
         */
        while (isspace(*str))
            str++;

        if (!*str || !isdigit(*str))
            break;

        switch(qualifier) {
        case 'h':
            if (is_sign) {
                short *s = (short *) va_arg(args,short *);
                *s = (short) simple_strtol(str,&next,base);
            } else {
                unsigned short *s = (unsigned short *) va_arg(args, unsigned short *);
                *s = (unsigned short) simple_strtoul(str, &next, base);
            }
            break;
        case 'l':
            if (is_sign) {
                long *l = (long *) va_arg(args,long *);
                *l = simple_strtol(str,&next,base);
            } else {
                unsigned long *l = (unsigned long*) va_arg(args,unsigned long*);
                *l = simple_strtoul(str,&next,base);
            }
            break;
        case 'L':
            if (is_sign) {
                long long *l = (long long*) va_arg(args,long long *);
                *l = simple_strtoll(str,&next,base);
            } else {
                unsigned long long *l = (unsigned long long*) va_arg(args,unsigned long long*);
                *l = simple_strtoull(str,&next,base);
            }
            break;
        case 'Z':
        {
            unsigned long *s = (unsigned long*) va_arg(args,unsigned long*);
            *s = (unsigned long) simple_strtoul(str,&next,base);
        }
        break;
        default:
            if (is_sign) {
                int *i = (int *) va_arg(args, int*);
                *i = (int) simple_strtol(str,&next,base);
            } else {
                unsigned int *i = (unsigned int*) va_arg(args, unsigned int*);
                *i = (unsigned int) simple_strtoul(str,&next,base);
            }
            break;
        }
        num++;

        if (!next)
            break;
        str = next;
    }
    return num;
}

/**
 * sscanf - Unformat a buffer into a list of arguments
 * @buf:    input buffer
 * @fmt:    formatting of buffer
 * @...:    resulting arguments
 */
int sscanf(const char * buf, const char * fmt, ...)
{
    va_list args;
    int i;

    va_start(args,fmt);
    i = vsscanf(buf,fmt,args);
    va_end(args);

    return i;
}

size_t strspn(const char *s, const char *accept)
{
    size_t l=0;
    int a=1,i, al=strlen(accept);

    while((a)&&(*s))
    {
        for(a=i=0;(!a)&&(i<al);i++)
            if (*s==accept[i]) a=1;
        if (a) l++;
        s++;
    }
    return l;
}

size_t strcspn(const char *s, const char *reject)
{
    size_t l=0;
    int a=1,i,al=strlen(reject);

    while((a)&&(*s))
    {
        for(i=0;(a)&&(i<al);i++)
            if (*s==reject[i]) a=0;
        if (a) l++;
        s++;
    }
    return l;
}

char*strtok_r(char*s,const char*delim,char**ptrptr)
{
    char*tmp=0;

    if (s==0) s=*ptrptr;
    s += strspn(s,delim);   /* overread leading delimiter */
    if (*s)
    {
        tmp=s;
        s+=strcspn(s,delim);

        if (*s) *s++=0;     /* not the end ? => terminate it */
    }
    *ptrptr=s;
    return tmp;
}

char *strtok(char *s, const char *delim)
{
    static char *strtok_pos;
    return strtok_r(s,delim,&strtok_pos);
}

char *strchr(const char *s1, int i)
{
    const unsigned char *s = (const unsigned char *)s1;
    unsigned char c = (unsigned int)i;

    while (*s && *s != c)
    {
        s++;
    }

    if (*s != c)
    {
        s = NULL;
    }

    return (char *) s;
}

long strtol(const char *str, char **endptr, int base)
{
    return simple_strtol(str, endptr, base);
}

long long strtoll(const char *str, char **endptr, int base)
{
    return simple_strtoll(str, endptr, base);
}

#endif
